package com.electronic.modules.electronicSealSignature;

import com.electronic.appearance.*;
import com.electronic.domain.*;
import com.electronic.exception.ResultCode;
import com.electronic.exception.ServiceException;
import com.electronic.signer.PdfExtendsSigner;
import com.electronic.utils.IFCAUtil;
import com.electronic.utils.ISvgUtil;
import com.electronic.utils.ImageDivider;
import com.itextpdf.io.image.ImageData;
import com.itextpdf.io.image.ImageDataFactory;
import com.itextpdf.kernel.exceptions.PdfException;
import com.itextpdf.kernel.geom.PageSize;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfPage;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.kernel.pdf.StampingProperties;
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
import com.itextpdf.kernel.pdf.extgstate.PdfExtGState;
import com.itextpdf.kernel.pdf.xobject.PdfFormXObject;
import com.itextpdf.kernel.pdf.xobject.PdfImageXObject;
import com.itextpdf.signatures.IExternalSignatureContainer;
import lombok.extern.slf4j.Slf4j;

import java.io.*;
import java.nio.file.FileSystemException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.cert.X509Certificate;
import java.util.*;

import static com.itextpdf.svg.converter.SvgConverter.convertToXObject;
/**
 * @Author: IFCA
 * @Description: 签章类
 */
@Slf4j
public class IFCASignature {

    /**
     * 组合外部签名的方法
     *
     * @param sourcePath    输入流
     * @param targetPath    输出流
     * @param signFieldName 签名字段名
     * @param pdfGmtSigner  签名器
     */
    public static void combineExternalSignature(Path sourcePath,
                                                Path targetPath,
                                                String signFieldName,
                                                PdfGmtSigner pdfGmtSigner)   {
        log.info("SpectreSealSignature.combineExternalSignature begin...");
        // 如果签名器为空，抛出运行时异常
        Objects.requireNonNull(pdfGmtSigner, "pdfGmtSigner is null!");
        log.info("pdfGmtSigner:{}", pdfGmtSigner);
        Objects.requireNonNull(sourcePath, "sourcePath is null!");
        log.info("sourcePath:{}", sourcePath);
        Objects.requireNonNull(targetPath, "targetPath is null!");
        log.info("targetPath:{}", targetPath);
        Objects.requireNonNull(signFieldName, "signFieldName is null!");
        log.info("signFieldName:{}", signFieldName);

        try (InputStream inputStream = Files.newInputStream(sourcePath);
             OutputStream outputStream = Files.newOutputStream(targetPath);
             PdfReader reader = new PdfReader(inputStream);
             PdfDocument pdfDoc = new PdfDocument(reader)) {
            // 执行延迟签名操作
            PdfExtendsSigner.signDeferred(pdfDoc, signFieldName, outputStream, pdfGmtSigner);
        }
        catch (FileSystemException e){

            // 抛出运行时异常，并附带异常信息
            throw new ServiceException(ResultCode.ERROR_SPECTRE_SEAL_SIGNATURE_NOT, ResultCode.ERROR_SPECTRE_SEAL_SIGNATURE_NOT.getMessage());
        }
        catch (Exception e) {
            // 打印异常栈跟踪
            log.error("SpectreSealSignature.combineExternalSignature 执行异常：", e);
            // 抛出运行时异常，并附带异常信息
            throw new ServiceException(ResultCode.ERROR_SPECTRE_SEAL_SIGNATURE_NOT, ResultCode.ERROR_SPECTRE_SEAL_SIGNATURE_NOT.getMessage());
        }
    }

    /**
     * 开始签署
     *
     * @param sourcePath      原始文件
     * @param targetPath      签署后文件
     * @param appearance      签名域外观对象
     * @param signatureConfig 签署配置
     */
    public static void sign(Path sourcePath, Path targetPath, AbstractAppearance appearance, SignatureConfig signatureConfig) {
        log.info("SpectreSealSignature.sign begin...");
        Objects.requireNonNull(sourcePath, "sourcePath is null!");
        Objects.requireNonNull(targetPath, "targetPath is null!");
        Objects.requireNonNull(appearance, "appearance is null!");
        Objects.requireNonNull(signatureConfig, "signatureConfig is null!");
        try {
            byte[] signedData = null;
            if (appearance instanceof NomalAppearance) {
                signedData = signPage(sourcePath, (NomalAppearance) appearance, signatureConfig);
            } else if (appearance instanceof CrossPageAppearance) {
                signedData = signCrossPage(sourcePath, (CrossPageAppearance) appearance, signatureConfig);
            } else if (appearance instanceof KeyWordsAppearance) {
                signedData = signKeyWords(sourcePath, (KeyWordsAppearance) appearance, signatureConfig);
            }
            Files.write(targetPath, signedData);
        } catch (Exception e) {
            throw new ServiceException(ResultCode.ERROR_SIGNATURE_CONFIG, ResultCode.ERROR_SIGNATURE_CONFIG.getMessage());

        }
    }

    /**
     * 骑缝章签署
     *
     * @param source              待签署文件
     * @param crossPageAppearance 骑缝章签名外观对象
     * @param signInfo            签署配置
     * @return byte[] 签署后文件字节数组
     */
    private static byte[] signCrossPage(Path source, CrossPageAppearance crossPageAppearance, SignatureConfig signInfo) throws Exception {
        // 创建 PdfReader 对象来读取文件
        try (PdfReader pdfReader = new PdfReader(Files.newInputStream(source));
             PdfDocument pdfDocument = new PdfDocument(pdfReader)) {


            // 获取起始页和结束页，如果未提供则设置默认值
            int startPageNo = Optional.ofNullable(crossPageAppearance.getStartPage())
                    .orElse(1);
            int endPageNo = Optional.ofNullable(crossPageAppearance.getEndPage())
                    .orElseGet(pdfDocument::getNumberOfPages);

            // 验证 startPageNo 和 endPageNo 的有效性
            if (startPageNo > endPageNo) {
                log.error("startPageNo (" + startPageNo + ") must be less than endPageNo (" + endPageNo + ")");
                throw new ServiceException(ResultCode.ERROR_PAGE, ResultCode.ERROR_PAGE.getMessage());
            }

            // 如果只有两页，则平均分
            if (endPageNo - startPageNo == 1) {
                crossPageAppearance.setFirstPagePercent(0.5f);
                crossPageAppearance.setLastPagePercent(0.5f);
            }

            // 获取签名字段名称，如果未提供则生成一个随机的 UUID
            String signFiledName = crossPageAppearance.getSignFieldName();
            if (Objects.isNull(signFiledName)) {
                signFiledName = UUID.randomUUID().toString();
            }

            // 获取图像字节数组
            byte[] imageBytes = crossPageAppearance.getStamperSr();
            // 判断是否为 SVG 格式
            boolean isSVG = ISvgUtil.isSvg(imageBytes);

            ArrayList<SubImage> subImages = ImageDivider.divideImage(pdfDocument, imageBytes, startPageNo, endPageNo, crossPageAppearance.getDpi(), crossPageAppearance.getRectangle(), crossPageAppearance.getFirstPagePercent(), crossPageAppearance.getLastPagePercent());

            int size = subImages.size();
            ArrayList<AbstractAppearance> list = new ArrayList<>();
            ImageData imageData = null;
            if (!isSVG) {
                imageData = ImageDataFactory.create(imageBytes);
            }

            // 处理分割后的图像
            for (int i = 0; i < size; ++i) {
                SubImage subImage = subImages.get(i);
                float realWidth = subImage.imageRect.getWidth();
                float min = 0.0F;
                if (realWidth < min) {
                    throw new ServiceException(ResultCode.ERROR_PDF_WIDTH,ResultCode.ERROR_PDF_WIDTH.getMessage());
                }

                float realHeight = subImage.imageRect.getHeight();
                if (realHeight < min) {
                    throw new ServiceException(ResultCode.ERROR_PAGE_HEIGHT,ResultCode.ERROR_PAGE_HEIGHT.getMessage());
                }

                crossPageAppearance.setPageNum(subImage.page);
                if (isSVG) {
                    crossPageAppearance.setStamperSr(imageBytes);
                } else {
                    crossPageAppearance.setImageData(imageData);
                }

                crossPageAppearance.setSignFieldName(signFiledName + "_" + i);
                if (crossPageAppearance.isMoveCrossPos()) {
                    if (Objects.isNull(crossPageAppearance.getRectangle())) {
//
                        throw new ServiceException(ResultCode.ERROR_RECTANGLE,ResultCode.ERROR_RECTANGLE.getMessage());

                    }
                    subImage.imageRect.setY(crossPageAppearance.getRectangle().getY());
                }

                crossPageAppearance.setRectangle(subImage.imageRect);
                crossPageAppearance.setSignOffsetX(subImage.xOffset);
                crossPageAppearance.setSignOffsetY(subImage.yOffset);
                //拷贝签名外观
                CrossPageAppearance appearance = (CrossPageAppearance) crossPageAppearance.clone();
                //旋转签名位置
                appearance = (CrossPageAppearance) IFCAUtil.rotateRectangle(pdfDocument, appearance);
                list.add(appearance);

            }

//            // 关闭 PdfReader
//            pdfReader.close();

            try (PdfReader reader = new PdfReader(Files.newInputStream(source))) {
                // 结合外部签名
                return combineExternalSignature(reader, list, signInfo, true, true);
            }

        }
    }

    /**
     * 进行签署的方法
     *
     * @param source          源文件
     * @param nomalAppearance 外观配置
     * @param signInfo        签名信息配置
     * @return 签署后的数据
     * @throws Exception 可能抛出的异常
     */
    private static byte[] signPage(Path source, NomalAppearance nomalAppearance, SignatureConfig signInfo) throws Exception {
        // 获取签名字段名，如果为空则生成随机的 UUID 字符串作为字段名
        String signFiledName = nomalAppearance.getSignFieldName();
        if (Objects.isNull(signFiledName)) {
            signFiledName = UUID.randomUUID().toString();
        }

        // 根据文件路径和设置的属性创建 PdfReader 对象
        try (PdfReader pdfReader = new PdfReader(Files.newInputStream(source));
             PdfDocument pdfDoc = new PdfDocument(pdfReader)) {
            // 获取印章图像字节数组
            byte[] imageBytes = nomalAppearance.getStamperSr();

            int[] pages = nomalAppearance.getPagesNums();
            ArrayList<AbstractAppearance> list = new ArrayList<>();
            // 遍历需要签署的页码
            for (int i = 0; i < pages.length; ++i) {
                int pageNo = pages[i];
                // 设置当前页面号
                nomalAppearance.setPageNum(pageNo);
                nomalAppearance.setStamperSr(imageBytes);
                // 设置签名字段名
                nomalAppearance.setSignFieldName(signFiledName + "_" + i);
                // 将当前外观克添加到列表
                NomalAppearance appearance = (NomalAppearance) nomalAppearance.clone();
                //旋转签名位置
                appearance = (NomalAppearance) IFCAUtil.rotateRectangle(pdfDoc, appearance);
                list.add(appearance);
            }

            try (PdfReader reader = new PdfReader(Files.newInputStream(source))) {
                // 调用另一个方法进行外部签名结合并返回结果
                return combineExternalSignature(reader, list, signInfo, false, false);
            }
        }
    }

    /**
     * 关键字签署
     *
     * @param source             待签署文件
     * @param keyWordsAppearance 关键字签名外观对象
     * @param signInfo           签名配置对象
     * @return byte[] 签署后文件字节数组
     */
    private static byte[] signKeyWords(Path source, KeyWordsAppearance keyWordsAppearance, SignatureConfig signInfo) throws Exception {
        // 获取签名字段名，如果为空则生成随机的 UUID 字符串作为字段名
        String signFiledName = keyWordsAppearance.getSignFieldName();
        if (Objects.isNull(signFiledName)) {
            signFiledName = UUID.randomUUID().toString();
        }

        //获取关键字位置
        Rectangle keyWordRect = keyWordsAppearance.getRectangle();

        // 获取印章图像字节数组
        byte[] imageBytes = keyWordsAppearance.getStamperSr();
        //获取关键字位置列表
        List<KeyWordsCoordinate> keyWordsCoordinateList = keyWordsAppearance.getKeyWordsCoordinate();
        //所有签名域
        ArrayList<AbstractAppearance> list = new ArrayList<>();

        // 根据文件路径和设置的属性创建 PdfReader 对象
        try (PdfReader pdfReader = new PdfReader(Files.newInputStream(source));
             PdfDocument pdfDoc = new PdfDocument(pdfReader)) {
            // 遍历需要签署的页码
            for (int i = 0; i < keyWordsCoordinateList.size(); ++i) {
                KeyWordsCoordinate keyWordsCoordinate = keyWordsCoordinateList.get(i);
                int pageNo = keyWordsCoordinate.getPage();
                // 设置当前页面号
                keyWordsAppearance.setPageNum(pageNo);
                keyWordsAppearance.setStamperSr(imageBytes);
                // 设置签名字段名
                keyWordsAppearance.setSignFieldName(signFiledName + "_" + i);
                //关键字位置
                Rectangle rectangle = new Rectangle(keyWordsCoordinate.getX(), keyWordsCoordinate.getY(), keyWordRect.getWidth(), keyWordRect.getHeight());
                keyWordsAppearance.setRectangle(rectangle);

                //将当前外观克隆并添加到列表
                KeyWordsAppearance appearance = (KeyWordsAppearance)keyWordsAppearance.clone();
                //旋转签名位置
                appearance = (KeyWordsAppearance) IFCAUtil.rotateRectangle(pdfDoc, appearance);
                list.add(appearance);
            }

            try (PdfReader reader = new PdfReader(Files.newInputStream(source))) {
                // 调用另一个方法进行外部签名结合并返回结果
                return combineExternalSignature(reader, list, signInfo, false, false);
            }
        }
    }

    /**
     * 组合外部签名的方法
     *
     * @param reader          PDF 读取器
     * @param appearanceList  外观列表
     * @param signatureConfig 签名配置
     * @param reuseImage      是否重用图像
     * @param isCross         是否跨页
     * @return 组合后的字节数组
     * @throws Exception 可能抛出的异常
     */
    private static byte[] combineExternalSignature(PdfReader reader, List<AbstractAppearance> appearanceList, SignatureConfig signatureConfig, boolean reuseImage, boolean isCross) throws Exception {
        // 创建字节数组输出流
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        // 获取外观列表的大小
        int size = appearanceList.size();
        // 创建盖章属性对象并设置相关属性
        StampingProperties properties = new StampingProperties().preserveEncryption();
        properties.useAppendMode();
        reader.setUnethicalReading(true);
        // 创建 PDF 签名器
        PdfExtendsSigner pdfSigner = new PdfExtendsSigner(reader, outputStream, properties);
        // 获取 PDF 文档
        PdfDocument pdfDocument = pdfSigner.getDocument();
        // 初始化偏移量 X 为 0
        float offsetX = 0.0F;
        // 初始化表单 X 对象为 null
        PdfFormXObject xObject = null;

        // 遍历外观列表
        for (int i = 0; i < size; i++) {
            AbstractAppearance appearance = appearanceList.get(i);
            Rectangle rectOne = appearance.getRectangle();
            // 如果矩形为空，抛出异常
            if (Objects.isNull(rectOne)) {
                throw new PdfException("签名位置信息不能为空!");
            }

            // 获取页面编号
            int pageNum = appearance.getPageNum();
            // 获取当前页面
            PdfPage currentPage = pdfDocument.getPage(pageNum);
            Rectangle mediaBox = currentPage.getMediaBox();
            PageSize currentPageSize = new PageSize(currentPage.getPageSizeWithRotation());
            pdfDocument.setDefaultPageSize(currentPageSize);

            // 创建 PDF 签名外观
            PdfSignatureAppearance signAppearance = new PdfSignatureAppearance(pdfDocument, new Rectangle(0.0F, 0.0F), pageNum);
            signAppearance.setPageNumber(pageNum);
            if (i == 0) {
                pdfSigner.setAppearance(signAppearance);
            }

            // 设置签名的原因和位置
            signAppearance.setReason((Objects.isNull(appearance.getReason())) ? "" : appearance.getReason());
            signAppearance.setLocation((Objects.isNull(appearance.getLocation())) ? "" : appearance.getLocation());
            signAppearance.setPageNumber(pageNum);
            // 获取签名字段名
            String fieldName = appearance.getSignFieldName();
            // 如果字段名为空或为空字符串，生成随机 UUID 并设置
            if (Objects.isNull(fieldName) || fieldName.trim().isEmpty()) {
                fieldName = UUID.randomUUID().toString();
                appearance.setSignFieldName(fieldName);
            }
            // 设置签名器的字段名
            pdfSigner.setFieldName(fieldName);
            // 创建新的矩形
            Rectangle rect = new Rectangle(rectOne);
            // 图像数据
            ImageData imageData;
            // 判断是否为 SVG 数据
            boolean isSvgData = appearance.isSvgImage();
            // 获取 SVG 数据
            byte[] svgData = appearance.getStamperSr();
            // 如果是 SVG 数据并且需要更改
            if (isSvgData) {
                // 获取 SVG 像素
                SealSize svgRect = ISvgUtil.getSvgSize(svgData);
                if (svgRect.getWidth() != 0.0F && svgRect.getHeight() != 0.0F) {
                    rectOne = new Rectangle(rectOne.getX(), rectOne.getY(), svgRect.getWidth() * 72.0F / 96.0F, svgRect.getHeight() * 72.0F / 96.0F);
                }

            }

            // 如果使用左上角坐标
            if (appearance.isUpperLeftCoordinate()) {
                rectOne = IFCAUtil.convertRectangleToTop(currentPage, currentPageSize, rectOne);
            }

            // 如果媒体框的 X 或 Y 不为 0
            if (mediaBox.getX() != 0.0F || mediaBox.getY() != 0.0F) {
                rectOne = new Rectangle(rectOne.getX() + mediaBox.getX(), rectOne.getY() + mediaBox.getY(), rectOne.getWidth(), rectOne.getHeight());
            }

            // 设置外观的矩形
            appearance.setRectangle(rectOne);
            // 获取签名证书
            X509Certificate signingCertificate = (X509Certificate) signatureConfig.getSigCert();

            // 如果证书不为空，设置签名创建者
            if (Objects.nonNull(signingCertificate)) {
                signAppearance.setSignatureCreator(signingCertificate.getSerialNumber().toString(16));
            }

            signAppearance.setPageRect(rectOne);
            signAppearance.setReuseAppearance(false);
            PdfFormXObject l2 = signAppearance.getLayer2();
            // 创建 PDF 画布
            PdfCanvas canvas2 = new PdfCanvas(l2, pdfDocument);

            // 创建 PDF 扩展图形状态
            PdfExtGState gs = new PdfExtGState();
            // 设置填充不透明度
            gs.setFillOpacity(appearance.getTransparency());
            // 设置画布的扩展图形状态
            canvas2.setExtGState(gs);

            // 获取图像数据
            imageData = appearance.getImageData();
            // 设置签名外观的渲染模式
            signAppearance.setRenderingMode(PdfSignatureAppearance.RenderingMode.GRAPHIC);

            // 创建 PDF 图像 X 对象
            PdfImageXObject imageXObject = null;

            // 如果图像数据不为空
            if (Objects.nonNull(imageData)) {
                // 如果重用图像
                if (reuseImage) {
                    imageXObject = new PdfImageXObject(imageData);
                }
                // 如果是骑缝章且重用图像
                if (isCross && reuseImage) {
                    float usableScale = Math.max(rect.getWidth() / imageData.getWidth(), rect.getHeight() / imageData.getHeight());
                    float w = imageData.getWidth() * usableScale;
                    float h = imageData.getHeight() * usableScale;
                    CrossPageAppearance crossPageAppearance = (CrossPageAppearance) appearance;
                    // 在画布上添加具有变换矩阵的 X 对象
                    canvas2.addXObjectWithTransformationMatrix(imageXObject, w, 0.0F, 0.0F, h, crossPageAppearance.getSignOffsetX(), crossPageAppearance.getSignOffsetY());
                } else {
                    // 计算可用比例
                    float usableScale = Math.min(rect.getWidth() / imageData.getWidth(), rect.getHeight() / imageData.getHeight());
                    float w = imageData.getWidth() * usableScale;
                    float h = imageData.getHeight() * usableScale;
                    float x = (rect.getWidth() - w) / 2.0F;
                    float y = (rect.getHeight() - h) / 2.0F;
                    //TODO 8585
                    if (reuseImage) {
                        canvas2.addXObjectWithTransformationMatrix(imageXObject, w, 0.0F, 0.0F, h, x, y);
                    } else {
                        canvas2.addImageWithTransformationMatrix(imageData, w, 0.0F, 0.0F, h, x, y);
                    }
                }
            } else if (isSvgData) {
                if (isCross) {
                    float clipWidth = rect.getWidth();
                    xObject = drawOnCanvas(new ByteArrayInputStream(svgData), canvas2, offsetX, 0.0F, xObject);
                    offsetX -= clipWidth;

                } else {
                    xObject = drawOnCanvas(new ByteArrayInputStream(svgData), canvas2, 0.0F, 0.0F, xObject);
                }
            }

            // 将外观添加到文档
            pdfSigner.addAppearanceToDocument(signAppearance, fieldName);
            if (i == size - 1) {
                pdfSigner.setAppearance(signAppearance);
                IExternalSignatureContainer container = signatureConfig.getGmtSigner();
                if (Objects.isNull(container)) {
                    container = new PdfGmtSigner(signatureConfig);
                }
                if (size > 1) {
                    pdfSigner.setSigOnce(true);
                }
                pdfSigner.signExternalContainer(container, signatureConfig.getPkcs7EstimatedSize());
            }
        }
        // 返回输出流的字节数组
        return outputStream.toByteArray();
    }

    /**
     * 在 PDF 画布上绘制图形的方法
     *
     * @param stream  输入流
     * @param canvas  PDF 画布
     * @param x       X 坐标
     * @param y       Y 坐标
     * @param xObject PDF 表单 X 对象
     * @return 绘制后更新的 PDF 表单 X 对象
     * @throws IOException 可能抛出的输入输出异常
     */
    public static PdfFormXObject drawOnCanvas(InputStream stream, PdfCanvas canvas, float x, float y, PdfFormXObject xObject) throws IOException {

        if (Objects.isNull(xObject)) {
            xObject = convertToXObject(stream, canvas.getDocument());
        }
        canvas.addXObjectAt(xObject, x, y);
        return xObject;
    }

}
